package com.eian.boot.metrics.aspect;

import com.eian.boot.common.id.IdHelper;
import com.eian.boot.metrics.annotation.PerformanceMonitor;
import com.eian.boot.metrics.collector.PerformanceCollector;
import com.eian.boot.metrics.config.MetricsProperties;
import com.eian.boot.metrics.model.PerformanceRecord;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 性能监控AOP切面
 *
 * @author alex.meng
 */
@Slf4j
@Aspect
@Component
@Order(1000) // 确保在其他切面之后执行
@ConditionalOnProperty(prefix = "eian.metrics", name = "enabled", havingValue = "true", matchIfMissing = true)
public class PerformanceMonitorAspect {

    private final MetricsProperties metricsProperties;

    @Autowired(required = false)
    private PerformanceCollector performanceCollector;

    public PerformanceMonitorAspect(MetricsProperties metricsProperties) {
        this.metricsProperties = metricsProperties;
    }

    /**
     * 拦截带有@PerformanceMonitor注解的方法
     */
    @Around("@annotation(performanceMonitor)")
    public Object monitorMethod(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor) throws Throwable {
        // 检查全局开关
        if (!metricsProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        // 检查注解开关
        if (!performanceMonitor.enabled()) {
            return joinPoint.proceed();
        }

        // 采样检查
        if (!shouldSample(performanceMonitor)) {
            return joinPoint.proceed();
        }

        return executeWithMonitoring(joinPoint, performanceMonitor);
    }

    /**
     * 拦截类级别的@PerformanceMonitor注解
     */
    @Around("@within(performanceMonitor)")
    public Object monitorClass(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor) throws Throwable {
        // 检查全局开关
        if (!metricsProperties.isEnabled()) {
            return joinPoint.proceed();
        }

        // 检查注解开关
        if (!performanceMonitor.enabled()) {
            return joinPoint.proceed();
        }

        // 检查方法是否已有注解（方法级注解优先级更高）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(PerformanceMonitor.class)) {
            return joinPoint.proceed();
        }

        // 采样检查
        if (!shouldSample(performanceMonitor)) {
            return joinPoint.proceed();
        }

        return executeWithMonitoring(joinPoint, performanceMonitor);
    }

    private Object executeWithMonitoring(ProceedingJoinPoint joinPoint, PerformanceMonitor performanceMonitor)
            throws Throwable {
        // 创建性能记录
        PerformanceRecord record = createPerformanceRecord(joinPoint, performanceMonitor);

        // 记录开始时间和内存
        LocalDateTime startTime = LocalDateTime.now();
        long startNanos = System.nanoTime();
        PerformanceRecord.MemoryInfo memoryInfo = null;

        if (metricsProperties.getPerformance().isMemoryMonitoring()) {
            memoryInfo = captureMemoryInfo();
        }

        Object result = null;
        Throwable exception = null;

        try {
            // 执行目标方法
            result = joinPoint.proceed();
            record.setSuccess(true);

            // 记录返回值
            if (shouldRecordResult(performanceMonitor)) {
                record.setResult(truncateIfNeeded(result));
            }

        } catch (Throwable throwable) {
            exception = throwable;
            record.setSuccess(false)
                    .setErrorMessage(throwable.getMessage())
                    .setErrorType(throwable.getClass().getSimpleName());
            throw throwable;
        } finally {
            // 计算执行时间
            long endNanos = System.nanoTime();
            long duration = (endNanos - startNanos) / 1_000_000; // 转换为毫秒
            LocalDateTime endTime = LocalDateTime.now();

            // 完善记录信息
            record.setStartTime(startTime)
                    .setEndTime(endTime)
                    .setDuration(duration);

            // 记录内存信息
            if (memoryInfo != null) {
                PerformanceRecord.MemoryInfo endMemoryInfo = captureMemoryInfo();
                memoryInfo.setAfterMemory(endMemoryInfo.getAfterMemory())
                        .setMemoryDelta(endMemoryInfo.getAfterMemory() - memoryInfo.getBeforeMemory());
                record.setMemoryInfo(memoryInfo);
            }

            // 记录线程信息
            record.setThreadInfo(captureThreadInfo());

            // 收集性能数据 - 仅在启用数据收集时调用
            if (metricsProperties.getCollect().isEnabled() && performanceCollector != null) {
                performanceCollector.collect(record);
            }

            // 输出日志
            logPerformanceRecord(record, performanceMonitor);
        }

        return result;
    }

    private PerformanceRecord createPerformanceRecord(ProceedingJoinPoint joinPoint,
                                                      PerformanceMonitor performanceMonitor) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        PerformanceRecord record = new PerformanceRecord()
                .setId(IdHelper.simpleUuid())
                .setClassName(signature.getDeclaringType().getSimpleName())
                .setMethodName(method.getName())
                .setMethodSignature(method.toString());

        // 设置监控名称
        String name = performanceMonitor.value();
        if (name.isEmpty()) {
            name = record.getClassName() + "." + record.getMethodName();
        }
        record.setName(name);

        // 记录方法参数
        if (shouldRecordArgs(performanceMonitor)) {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                List<Object> argList = Arrays.stream(args)
                        .map(this::truncateIfNeeded)
                        .toList();
                record.setArgs(argList);
            }
        }

        // 设置业务标签
        if (performanceMonitor.tags().length > 0) {
            record.setTags(Arrays.asList(performanceMonitor.tags()));
        }

        return record;
    }

    private boolean shouldSample(PerformanceMonitor performanceMonitor) {
        double sampleRate = performanceMonitor.sampleRate();
        if (sampleRate < 0) {
            sampleRate = metricsProperties.getPerformance().getSampleRate();
        }

        if (sampleRate >= 1.0) {
            return true;
        }

        if (sampleRate <= 0.0) {
            return false;
        }

        return ThreadLocalRandom.current().nextDouble() < sampleRate;
    }

    private boolean shouldRecordArgs(PerformanceMonitor performanceMonitor) {
        return performanceMonitor.recordArgs() || metricsProperties.getPerformance().isRecordArgs();
    }

    private boolean shouldRecordResult(PerformanceMonitor performanceMonitor) {
        return performanceMonitor.recordResult() || metricsProperties.getPerformance().isRecordResult();
    }

    private Object truncateIfNeeded(Object obj) {
        if (obj == null) {
            return null;
        }

        String str = obj.toString();
        int maxLength = metricsProperties.getPerformance().getMaxRecordLength();

        if (str.length() > maxLength) {
            return str.substring(0, maxLength) + "...";
        }

        return obj;
    }

    private PerformanceRecord.MemoryInfo captureMemoryInfo() {
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        long maxMemory = runtime.maxMemory();

        return new PerformanceRecord.MemoryInfo()
                .setBeforeMemory(usedMemory)
                .setAfterMemory(usedMemory) // 将在finally块中更新
                .setTotalMemory(totalMemory)
                .setFreeMemory(freeMemory)
                .setMaxMemory(maxMemory);
    }

    private PerformanceRecord.ThreadInfo captureThreadInfo() {
        Thread currentThread = Thread.currentThread();
        return new PerformanceRecord.ThreadInfo()
                .setThreadId(currentThread.getId())
                .setThreadName(currentThread.getName())
                .setVirtual(currentThread.isVirtual());
    }

    private void logPerformanceRecord(PerformanceRecord record, PerformanceMonitor performanceMonitor) {
        if (!metricsProperties.getLog().isEnabled()) {
            return;
        }

        String level = metricsProperties.getLog().getLevel();
        String message = formatLogMessage(record);

        // 检查是否为慢方法
        long slowThreshold = performanceMonitor.slowThreshold() > 0
                ? performanceMonitor.slowThreshold()
                : metricsProperties.getPerformance().getSlowThreshold();

        boolean isSlow = record.getDuration() > slowThreshold;

        switch (level.toUpperCase()) {
            case "DEBUG":
                log.debug(message);
                break;
            case "INFO":
                if (isSlow) {
                    log.warn("[SLOW] " + message);
                } else {
                    log.info(message);
                }
                break;
            case "WARN":
                log.warn(message);
                break;
            case "ERROR":
                if (!record.isSuccess()) {
                    log.error(message);
                }
                break;
        }
    }

    private String formatLogMessage(PerformanceRecord record) {
        String pattern = metricsProperties.getLog().getPattern();

        return pattern
                .replace("{method}", record.getClassName() + "." + record.getMethodName())
                .replace("{duration}", String.valueOf(record.getDuration()))
                .replace("{success}", String.valueOf(record.isSuccess()))
                .replace("{thread}",
                        record.getThreadInfo() != null ? record.getThreadInfo().getThreadName() : "unknown");
    }
}
